//
// Unihan Input Method
//
// Copyright © 2002-2007 Sun Wah Linux Ltd.
//
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
// USA
//
// $Id: core.c 804 2007-07-31 15:23:43Z roger $
//

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#define LOG_DOMAIN "core"
#include "core.h"
#include "ui.h"
#include "im_methods.h"
#include "unihan_xaux.h"
#include "unicode.h"
#include "phrase.h"
#include "log.h"
#include <unihan_im.h>

#include <dirent.h>
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>

#if TIMEBOMB
#  include <time.h>
#endif

#define IM_MODULES_DIR "/usr/lib/unihan2/im_modules"

typedef struct _IMModuleList IMModuleList;
typedef struct _IMList IMList;

struct _IMModuleList {
	char *fullpath;
	void *dlh;
	char *id;
	char *name;
	char *version;
	int (*init)(void);
	void (*done)(void);
	IMModuleList *prev;
	IMModuleList *next;
};

struct _IMList {
	int num;
	char *id;
	char *name;
	char *version;
	void *imUserData;
	IMMethods *m;
	void *userData;
	int initialized;
	IMModuleList *imModule;
    int lookup_cands_per_page;

#if 0
	UnihanPropertyArgList imPropertyList;
#endif

	IMList *prev;
	IMList *next;
};

typedef struct {
	IMModuleList *imModules;
	IMList *inputMethods;
	int numIM;
	UnihanMethods unihanMethodsTable;
    char *defaultIM;
} IMManagerData;

static int im_module_get_info(IMManagerData *d, IMModuleList *mod);
static int im_notify_switch_event(iml_session_t *s, int on);
static int im_propagate_config_change_event(iml_session_t *s, IMList *im, char *key);

#if 0
static int im_propagate_property_change_event(iml_session_t *s, IMList *im, char *key, int value);
#endif
static int has_add_upim = 0;

#if NEW_PROPERTY
static int im_propagate_property_toggled_event(iml_session_t *s, IMList *im, char *key);
static int im_propagate_property_register_event(iml_session_t *s, IMList *im, char *imid);
#endif

static void init_uim(UnihanIM *uim, IMList *im, iml_session_t *s);


/****************************************************************************/
/**
 * @brief Starts conversion (input processing)
 *
 * Asks IIIMF to begin sending input events to us, notifies the current 
 * input method, and updates the GUI as necessary.
 *
 * @param s     current IMLSession
 * @see conversion_off()
 */
void
conversion_on(iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	//DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	iml_inst *lp, *rrv = NULL;
#if 0
	UnihanPropertyArgList prop_list;
#endif

#if TIMEBOMB
	if (check_expired(s->desktop)) {
		notify_expired(s);
		return;
	}
#endif

	lp = s->If->m->iml_make_start_conversion_inst(s);
	s->If->m->iml_link_inst_tail(&rrv, lp);
	s->If->m->iml_execute(s, &rrv);

	ds->conv_on = 1;

	status_update(s);
	im_notify_switch_event(s, 1);

#if 0
	prop_list = im_get_current_im_props(s);

	while(prop_list->type) {
		if(prop_list->type == UNIHAN_PROPERTY_RUNTIME) {
			switch (prop_list->UnihanProperty.rt_property.rt_type) {
			case UNIHAN_RUNTIME_PROPERTY_FULL_HALF:
				if(prop_list->UnihanProperty.rt_property.value) {

					im_set_width(s, 1);
				}
				break;
			case UNIHAN_RUNTIME_PROPERTY_PUNC_EN_CH:
				if(prop_list->UnihanProperty.rt_property.value) {
					im_set_puncce(s, 1);
				}
				break;
			case UNIHAN_RUNTIME_PROPERTY_SOFT_KB:
				if(prop_list->UnihanProperty.rt_property.name.nr_name ==
						UNIHAN_SOFTKB_US) {
					if(prop_list->UnihanProperty.rt_property.value) {
						us_keyboard_show(s);
						ds->us_kbd = 1;
					}
				} else if(prop_list->UnihanProperty.rt_property.name.nr_name ==
						UNIHAN_SOFTKB_SYMBOL) {
					if(prop_list->UnihanProperty.rt_property.value) {
						punc_keyboard_show(s);
						ds->punctuation = 1;
					}
				}
				break;
			case UNIHAN_RUNTIME_PROPERTY_TCSC:
				if(prop_list->UnihanProperty.rt_property.value) {
					dd->tcsc = prop_list->UnihanProperty.rt_property.value;
					tcsc_set(s, dd->tcsc);
				}
				break;
			case UNIHAN_RUNTIME_PROPERTY_STATE_EN_CH:
			default:
				break;
			} // switch
		}
		prop_list = prop_list->next;
	}
#endif
#if NEW_PROPERTY	
#else
    if (!ds->puncce)
		toggle_puncce(s);

	if (ds->fullwidth)
		toggle_width(s);
#endif

	if (ds->punctuation)
        punc_keyboard_show(s);

	if (ds->us_kbd)
		us_keyboard_show(s);

/* Delete these two line to default fullwidth switching following with conversion on/off
        if (!ds->fullwidth)
                toggle_width(s);
2004.2.10 arren*/

	// this is last
	tooltip_update_im(s);
}


/****************************************************************************/
/**
 * @brief Ends conversion (input processing)
 *
 * Asks IIIMF to stop sending input events to us, notifies the current 
 * input method, and updates the GUI as necessary.
 *
 * @param s     current IMLSession
 * @see conversion_on()
 */
void
conversion_off(iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
    iml_inst *lp, *rrv = NULL;

    lp = s->If->m->iml_make_preedit_done_inst(s);
    s->If->m->iml_link_inst_tail(&rrv, lp);
    ds->ui_started &= ~PREEDIT_STARTED;
		
    int int_values[1] = { UNIHAN_AUX_PREEDIT_HIDE };
    aux_draw(s, AUX_LOOKUP_CLASS_NAME, 1, int_values, 0, NULL);
		
	lp = s->If->m->iml_make_end_conversion_inst(s);
	s->If->m->iml_link_inst_tail(&rrv, lp);
	s->If->m->iml_execute(s, &rrv);

	ds->conv_on = 0;

	status_update(s);
	im_notify_switch_event(s, 0);

	if (ds->punctuation) {
		//ds->punctuation = 0;
		punc_keyboard_hide(s);
	}

	//if (ds->us_kbd) {
	//	ds->us_kbd = 0;
	//	us_keyboard_hide(s);
	//}
	//
	if (ds->phrase_state == PHRASE_STATE_SELECTING) {
		phrase_matcher_hide_results(s);
		ds->phrase_state = PHRASE_STATE_IDLE;
	}

#if NEW_PROPERTY
#else	
	if (ds->puncce)
		toggle_puncce(s);

    if (ds->fullwidth)
        toggle_width(s);
#endif

	tooltip_update_im(s);
}


/**
 * @brief Temporarily suspends conversion (input processing)
 *
 * Temporarily switches off input method, but does not ask IIIMF to stop
 * sending input events to us. The GUI is updated to indicate input method is
 * switched off. For example, this is used by the single Shift-key input
 * toggle.
 *
 * @param s     current IMLSession
 * @see conversion_temp_on()
 * @see conversion_on()
 * @see conversion_off()
 */
void
conversion_temp_off(iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	ds->conv_on = -1;
	if (ds->phrase_state == PHRASE_STATE_SELECTING)
		phrase_matcher_hide_results(s);
	im_notify_switch_event(s, 0);
	tooltip_update_im(s);
}


/**
 * @brief Temporarily resumes conversion (input processing)
 *
 * Temporarily switches on input method. Assumes IIIMF is already sending input
 * events to us; no checking is done. The GUI is updated to indicate input
 * method is switched on. For example, this is used by the single Shift-key
 * input toggle.
 *
 * @param s     current IMLSession
 * @see conversion_temp_off()
 * @see conversion_on()
 * @see conversion_off()
 */
void
conversion_temp_on(iml_session_t *s)
{
    DataPerSession *ds = (DataPerSession *)s->specific_data;
	ds->conv_on = 1;
	if (ds->phrase_state == PHRASE_STATE_SELECTING)
		phrase_matcher_show_results(s);
	im_notify_switch_event(s, 1);
	tooltip_update_im(s);
}


/****************************************************************************/
/**
 * @defgroup IMManager IM Manager
 *
 * The IM Manager handles the details of loading/unloading IMs inside IM
 * modules.
 */
/**
 * @brief Initialises IM Manager
 * @ingroup IMManager
 *
 * During initialisation, the IM Manager searches for IM modules and queries
 * each module what input methods they implement.
 *
 * @param desktop   current IMLDesktop
 * @see im_manager_done()
 */
void
im_manager_init(iml_desktop_t *desktop)
{
	DataPerDesktop *dtdata = (DataPerDesktop *)desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	DIR *dir;
	union {
		struct dirent d;
		char b[sizeof(struct dirent) + NAME_MAX + 1];
	} u;
	struct dirent *res = NULL;

	if (d) return;

	d = (IMManagerData *)malloc(sizeof(IMManagerData));
	memset(d, 0, sizeof(IMManagerData));
	dtdata->im_manager_data = d;
	d->numIM = 0;

	d->unihanMethodsTable = unihan_methods_table;

	dir = opendir(IM_MODULES_DIR);
	if (!dir)
		return;

	while (readdir_r(dir, &u.d, &res) == 0 && res != NULL) {
		const char *dlerrmsg = NULL;
		IMModuleList *new = NULL;
		char *ext = strrchr(res->d_name, (int)'.');
		if (!ext || strcmp(".so", ext) != 0)
			continue;

		unihan_message("found module: %s", res->d_name);

		new = (IMModuleList*)malloc(sizeof(IMModuleList));
		if (!new)
			return;
		memset(new, 0, sizeof(IMModuleList));
		new->fullpath = (char*)malloc(strlen(IM_MODULES_DIR)
				+ strlen(res->d_name) + 2);
		if (!new->fullpath) {
			free(new);
			return; 	//why don't continue????
			//continue;
		}

		strcpy(new->fullpath, IM_MODULES_DIR "/");
		strcat(new->fullpath, res->d_name);
		new->dlh = dlopen(new->fullpath, RTLD_LAZY);
		if (!new->dlh)
			goto module_open_error;

		new->init = dlsym(new->dlh, "unihan_im_module_init");
		dlerrmsg = dlerror();
		if (dlerrmsg != NULL)
			goto module_open_error;
		new->done = dlsym(new->dlh, "unihan_im_module_done");
		dlerrmsg = dlerror();
		if (dlerrmsg != NULL)
			goto module_open_error;

		if (new->init)
			if ((*new->init)() == 0)
				goto module_open_error;

		if (im_module_get_info(d, new) == 0) {
			if (new->done)
				(*new->done)();
			goto module_open_error;
		}

		if (d->imModules) d->imModules->prev = new;
		new->next = d->imModules;
		d->imModules = new;
		d->imModules->prev = NULL;
		if (!strcmp(res->d_name, "upim.so")) {
			has_add_upim = 1;
		}
		continue;

module_open_error:
		if (new && new->dlh) dlclose(new->dlh);
		if (new && new->fullpath) free(new->fullpath);
		if (new) free(new);
		continue;
	}
	closedir(dir);
}


/****************************************************************************/
/**
 * @brief Finalises IM Manager
 * @ingroup IMManager
 *
 * If there are still IMs loaded, each IM is finalises by calling its done()
 * method. Similarly for IM modules.
 *
 * @param desktop   current IMLDesktop
 * @see im_manager_init()
 */
void
im_manager_done(iml_desktop_t *desktop)
{
	DataPerDesktop *dtdata = (DataPerDesktop *)desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im;
	IMModuleList *immod;

	if (!d) return;

	im = d->inputMethods;
	while (im) {
        IMConfigList *imConfig = dtdata->imConfig[im->num];
        while (imConfig) {
            if (imConfig->key) free(imConfig->key);
            if (imConfig->value) free(imConfig->value);

            IMConfigList *foo = imConfig;
            imConfig = imConfig->next;
            free(foo);
        }

		if (im->m->done && im->initialized) {
			UnihanIM uim;
			uim.privateData = NULL;
			uim.id = im->id;
            uim.module_id = im->imModule->id;
			uim.host_name = desktop->host_name;
			uim.user_name = desktop->user_name;
			uim.imUserData = im->imUserData;
			uim.desktopUserData = im->userData;
			uim.sessionUserData = NULL;
			uim.m = &d->unihanMethodsTable;
			im->m->done(&uim);
		}
		if (im->id) free(im->id);
		if (im->name) free(im->name);
		if (im->version) free(im->version);
		if (im->imUserData) free(im->imUserData);
		if (im->m) free(im->m);

		IMList *foo = im;
		im = im->next;
		free(foo);
	}

	immod = d->imModules;
	while (immod) {
		if (immod->done)
			immod->done();
		dlclose(immod->dlh);
		if (immod->id) free(immod->id);
		if (immod->name) free(immod->name);
		if (immod->version) free(immod->version);
		if (immod->fullpath) free(immod->fullpath);

		IMModuleList *foo = immod;
		immod = immod->next;
		free(foo);
	}

    if (d->defaultIM) free(d->defaultIM);
	if (d) free(d);
	dtdata->im_manager_data = NULL;
}


/****************************************************************************/
/**
 * @brief Get number of IMs
 * @ingroup IMManager
 *
 * @param desktop   current IMLDesktop
 *
 * @returns the total number of IMs known to the IM Manager
 */
int
im_manager_get_num_im(iml_desktop_t *desktop)
{
	DataPerDesktop *dtdata = (DataPerDesktop *)desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	return d->numIM;
}


/****************************************************************************/
/**
 * @defgroup IM Input Method handling
 *
 * These functions handle the propagation of events to IM, and some convenience
 * functions for getting and setting the current IM.
 */
/**
 * @brief Get name of current IM
 * @ingroup IM
 *
 * @param s     current IMLSession
 *
 * @returns the name of the current IM. This string is owned by IM Manager 
 *          and must not be modified or freed by the caller.
 */
const char *
im_get_curr_im_name(iml_session_t *s)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	return ((IMList*)scdata->currIM)->name;
}


/****************************************************************************/
/**
 * @brief Returns the list of IMs to Aux
 * @ingroup IMManager
 *
 * @param s     current IMLSession
 * @param aux   incoming Aux event data (unused)
 */
void
im_manager_get_im_list(iml_session_t *s, IMAuxDrawCallbackStruct *aux)
{
        int int_values[1];
        UTFCHAR **string_values;
        DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
        IMManagerData *d = (IMManagerData *)dd->im_manager_data;
        IMList *im = d->inputMethods;
        int i;

        int_values[0] = UNIHAN_ENGINE_GET_IM_LIST;

        unihan_debug("sending im list to aux, im_count = %d", d->numIM);

        i = d->numIM;
        string_values = (UTFCHAR**)s->If->m->iml_new(s, sizeof(UTFCHAR*)*i*2);
        memset(string_values, 0, sizeof(UTFCHAR*) * i * 2);
        for (i = 0; im; i++, im = im->next) {
                string_values[i*2] = utf8_to_utf16be(s, (UTF8CHAR *)im->id, NULL);
                string_values[i*2+1] = utf8_to_utf16be(s, (UTF8CHAR *)im->name, NULL);
        }

        aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 
                                           d->numIM * 2, string_values);
}


typedef struct _DefaultIM {
	char *locale;
	char *im;
} DefaultIM;
/****************************************************************************/
/**
 * @brief Creates IM Manager session context
 * @ingroup IMManager
 *
 * The IM Manager keeps some of its own data about each IMLSession. Therefore,
 * whenever an IMLSession is created this function must be called.
 *
 * @param s     current IMLSession
 *
 * @see im_manager_destroy_session()
 */
void
im_manager_create_session(iml_session_t *s)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im = d->inputMethods;
	char *default_im = d->defaultIM;
	DefaultIM *foo, default_im_list[] = {
		{ "zh_CN", "itabc" },
		{ "zh_HK", "jyutping0" },
		{ "zh_TW", "changjei" },
		{ NULL, NULL }
	};

        if (!default_im) {
                foo = default_im_list;
                while (foo->locale) {
                        if (!strcasecmp(foo->locale, s->If->locale)) {
                                default_im = foo->im;
                                break;
                        }
                        foo++;
                }
                if (!default_im)
                        default_im = "itabc";
        }

	// now find that IM
	while (im) {
		if (!strcasecmp(default_im, im->id))
			break;
		im = im->next;
	}
	if (!im)
		im = d->inputMethods;  // must set to at least something

    unihan_debug("im_manager_create_session(): default IM is %s", im ? im->id : "<NULL>");

	scdata->currIM = im;

    if (dtdata->phrase_mode == 1) {
        scdata->phrase_state = PHRASE_STATE_IDLE;
    }

#if 0
	scdata->currIMprop_list = properties_dup(im->imPropertyList);	
#endif	
}


/****************************************************************************/
/**
 * @brief Destroys IM Manager session context
 * @ingroup IMManager
 *
 * The IM Manager keeps some of its own data about each IMLSession. Therefore,
 * whenever an IMLSession is about to be destroyed this function must be
 * called.
 *
 * @param s     current IMLSession
 *
 * @see im_manager_create_session()
 */
void
im_manager_destroy_session(iml_session_t *s)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im = d->inputMethods;

	unihan_debug("im_manager_destroy_session()");

    if (is_preedit_started(scdata->ui_started)) {
        iml_inst *lp, *rrv = NULL;

        if (0) { //(scdata->preedit_started != -1) {
            lp = s->If->m->iml_make_preedit_erase_inst(s);
            s->If->m->iml_link_inst_tail(&rrv, lp);
        }
        lp = s->If->m->iml_make_preedit_done_inst(s);
        s->If->m->iml_link_inst_tail(&rrv, lp);
        s->If->m->iml_execute(s, &rrv);

        scdata->ui_started &= ~PREEDIT_STARTED;
    }

	// let each IM free their session-specific data
	while (im) {
		if (scdata->userData[im->num].created) {
			UnihanIM uim;
            init_uim(&uim, im, s);
			im->m->destroy_session(&uim);
		}

		im = im->next;
	}
}


static void
init_uim(UnihanIM *uim, IMList *im, iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dd->im_manager_data;

    if (!uim || !im || !s)
        return;
    memset(uim, 0, sizeof(UnihanIM));
    uim->privateData = s;
    uim->id = im->id;
    uim->module_id = im->imModule->id;
    uim->host_name = s->desktop->host_name;
    uim->user_name = s->desktop->user_name;
    uim->imUserData = im->imUserData;
    uim->desktopUserData = im->userData;
    uim->sessionUserData = ds->userData[im->num].data;
    uim->m = &d->unihanMethodsTable;
}


/****************************************************************************/
/**
 * @brief Sets the default IM for this session
 * @ingroup IMManager
 *
 * The default IM is the IM selected when user presses Ctrl-Space.
 *
 * @param s     current IMLSession
 * @param imid  IM ID string
 */
void
im_manager_set_default_im(iml_session_t *s, char *imid)
{
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;

    if (d->defaultIM)
        free(d->defaultIM);

    d->defaultIM = strdup(imid);
}


/****************************************************************************/
static int
prepare_im(IMList *im, iml_session_t *s, IMManagerData *d, DataPerSession *scdata)
{
	UnihanIM uim;

	if (!im)
		return False;

	uim.privateData = s;
	uim.id = im->id;
    uim.module_id = im->imModule->id;
	uim.host_name = s->desktop->host_name;
	uim.user_name = s->desktop->user_name;
	uim.imUserData = im->imUserData;
	uim.desktopUserData = NULL;
	uim.sessionUserData = NULL;
	uim.m = &d->unihanMethodsTable;

	if (!im->initialized) {
		if (!im->m->init(&uim)) {
			return False;
		}
		im->userData = uim.desktopUserData;
		im->initialized = True;
	} else {
		uim.desktopUserData = im->userData;
	}
	if (!scdata->userData[im->num].created) {
		if (!im->m->create_session(&uim)) {
			return False;
		}
		scdata->userData[im->num].data = uim.sessionUserData;
		scdata->userData[im->num].created = True;
	}
	return True;
}


/****************************************************************************/
/**
 * @brief Propagates focus event to IM
 * @ingroup IM
 *
 * @param s        current IMLSession
 * @param focused  1 = focus in, 0 = focus out
 *
 * @returns %True if handled by IM, %False otherwise.
 */
int
im_propagate_focus_event(iml_session_t *s, int focused)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im = (IMList *)scdata->currIM;

    if (focused) {
		check_module_info(s);
		status_update(s);
        if (scdata->conv_on)
            tooltip_update_im(s);
    } else {
        int int_values[1] = { UNIHAN_AUX_UNSET_FOCUS };
        aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);
        //lost_focus_notify(s);
    }

	// ensure IM is init'd for desktop and session
	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
        init_uim(&uim, im, s);

		IMFocusEvent imfev;
		imfev.type = IM_FOCUS;
		imfev.focused = focused;

		return im->m->handle_event(&uim, (IMEvent *)&imfev);
	}

	if (!focused) {
		//FIXME :
		//force to preedit_erase() and lookup_done();
	}
	return False;
}


/****************************************************************************/
/**
 * @brief Propagates key event event to IM
 * @ingroup IM
 *
 * @param s     current IMLSession
 * @param kev   IMKeyListEvent
 *
 * @bug Only the first key event in IMKeyListEvent will be propagated even if
 *      there are more than one event in the list.
 *
 * @returns %True if handled by IM, %False otherwise.
 */
int
im_propagate_key_event(iml_session_t *s, IMKeyListEvent *kev)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im = (IMList *)scdata->currIM;

	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
        init_uim(&uim, im, s);

		IMKeyEvent imkev;
		imkev.type = IM_KEY;
		imkev.keyCode = kev->keylist[0].keyCode;
		imkev.keyChar = kev->keylist[0].keyChar;
		imkev.modifier = kev->keylist[0].modifier;

        // don't pass along unwanted modifiers to prevent confusing
        // the input module
        imkev.modifier &= ( IM_ALT_GRAPH_MASK | IM_ALT_MASK | IM_CTRL_MASK | IM_META_MASK | IM_SHIFT_MASK );

		return im->m->handle_event(&uim, (IMEvent *)&imkev);
	}
	return False;
}


/****************************************************************************/
/**
 * @brief Notifies IM it has been switched on or off
 * @ingroup IM
 *
 * @param s     current IMLSession
 * @param on    0 = switched off, 1 = switched on
 *
 * @returns %True if handled by IM, %False otherwise.
 */
static int
im_notify_switch_event(iml_session_t *s, int on)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im = (IMList *)scdata->currIM;

	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
        init_uim(&uim, im, s);

		IMSwitchEvent sev;
		sev.type = IM_SWITCH;
		sev.on = on;

		return im->m->handle_event(&uim, (IMEvent *)&sev);
	}

	return False;
}


/****************************************************************************/
/**
 * @brief Changes current IM to the next IM in list
 * @ingroup IM
 *
 * The list of IMs considered is defined by the user.
 *
 * @bug Currently this asks Aux to change to the next IM because the IM list
 *      is inside Aux. This is very wrong.
 *
 * @param s     current IMLSession
 */
void
im_change_to_next_im(iml_session_t *s)
{
	//DataPerSession *scdata = (DataPerSession *)s->specific_data;

	int int_values[1];
	//UTFCHAR *string_values[1];

	check_module_info(s);
	int_values[0] = UNIHAN_AUX_STATUS_NEXT_IM;

	aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);

	//if (scdata->punctuation) {
	//	scdata->punctuation = 0;
	//	punc_keyboard_hide(s);
	//}

	//if (scdata->us_kbd) {
	//	scdata->us_kbd = 0;
	//	us_keyboard_hide(s);
	//}
}

/****************************************************************************/
/**
 * @brief Changes current IM to the next IM in list
 * @ingroup IM
 *
 * The list of IMs considered is the full list from IM Manager.
 *
 * @param s     current IMLSession
 */
void
im_change_to_next_im1(iml_session_t *s)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;

	im_notify_switch_event(s, 0);

	if (scdata->currIM) {
		scdata->currIM = ((IMList *)scdata->currIM)->next;
	}
	if (!scdata->currIM) {
		scdata->currIM = d->inputMethods;
	}
#if 0
	destroy_im_property(scdata->currIMprop_list);
	fprintf(stderr, "UNIHAN LE : Duplicate imPropertyList in im_change_to_next_im1().\n\r");
	scdata->currIMprop_list = properties_dup(((IMList *)scdata->currIM)->imPropertyList);
#endif
	im_notify_switch_event(s, 1);

	status_update(s);
	tooltip_update_im(s);
}

/****************************************************************************/
/**
 * @brief Changes current IM to the previous IM in list
 * @ingroup IM
 *
 * The list of IMs considered is defined by the user.
 *
 * @bug Currently this asks Aux to change to the next IM because the IM list
 *      is inside Aux. This is very wrong.
 *
 * @param s     current IMLSession
 */
void
im_change_to_prev_im(iml_session_t *s)
{
	//DataPerSession *scdata = (DataPerSession *)s->specific_data;

	int int_values[1];
	//UTFCHAR *string_values[1];

	check_module_info(s);
	int_values[0] = UNIHAN_AUX_STATUS_PREV_IM;

	aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);

	//if (scdata->punctuation) {
	//	scdata->punctuation = 0;
	//	punc_keyboard_hide(s);
	//}

	//if (scdata->us_kbd) {
	//	scdata->us_kbd = 0;
	//	us_keyboard_hide(s);
	//}
}

/****************************************************************************/
/**
 * @brief Changes current IM to the previous IM in list
 * @ingroup IM
 *
 * The list of IMs considered is the full list from IM Manager.
 *
 * @param s     current IMLSession
 */
void
im_change_to_prev_im1(iml_session_t *s)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;

	im_notify_switch_event(s, 0);

	if (scdata->currIM)
		scdata->currIM = ((IMList *)scdata->currIM)->prev;
	if (!scdata->currIM) {
		scdata->currIM = d->inputMethods;
		if (!scdata->currIM) return;
		while (((IMList *)scdata->currIM)->next)
			scdata->currIM = ((IMList *)scdata->currIM)->next;
	}
#if 0
	destroy_im_property(scdata->currIMprop_list);
	fprintf(stderr, "UNIHAN LE : Duplicate imPropertyList in im_change_to_prev_im1().\n\r");
	scdata->currIMprop_list = properties_dup(((IMList *)scdata->currIM)->imPropertyList);
#endif
	im_notify_switch_event(s, 1);

	status_update(s);
	tooltip_update_im(s);
}

/****************************************************************************/
/**
 * @brief Changes current IM by ID
 * @ingroup IM
 *
 * @param s     current IMLSession
 * @param imid  IM ID string
 */
void
im_change_to_im(iml_session_t *s, char *imid)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im = d->inputMethods;

	unihan_debug("im requested: %s", imid);

	while (im) {
		if (!strcasecmp(imid, im->id))
			break;
		im = im->next;
	}
	if (!im)
		return;

	unihan_debug("im %s found", im->id);
    im_notify_switch_event(s, 0);

    scdata->currIM = im;

#if 0        
	destroy_im_property(scdata->currIMprop_list);
	scdata->currIMprop_list = properties_dup(im->imPropertyList);
#endif        
	im_notify_switch_event(s, 1);

#if 0        
	im_set_property_list(s ,scdata->currIMprop_list);
#endif

	check_module_info(s);
	status_update(s);
	tooltip_update_im(s);

#if NEW_PROPERTY 
#else
	
	//check_fullwidth(s);
	//check_puncce(s);

	//if (scdata->punctuation) {
	//	scdata->punctuation = 0;
	//	punc_keyboard_hide(s);
	//}

	//if (scdata->us_kbd) {
	//	scdata->us_kbd = 0;
	//	us_keyboard_hide(s);
	//}
#endif
}

/****************************************************************************/
static int
im_module_get_info(IMManagerData *d, IMModuleList *mod)
{
	int (*unihan_im_module_get_info)(UnihanIMModuleArgList *args);
	UnihanIMModuleArgList args[] = {
		{ UNIHAN_IM_MODULE_ID, NULL },
		{ UNIHAN_IM_MODULE_NAME, NULL },
		{ UNIHAN_IM_MODULE_VERSION, NULL },
		{ UNIHAN_IM_MODULE_IM_LIST, NULL },
		{ 0, NULL },
	};
	int ret;
	int num_im = 0;

	if (!d) return 0;
	if (!mod) return 0;
	if (!mod->dlh) return 0;

	unihan_im_module_get_info = dlsym(mod->dlh, "unihan_im_module_get_info");
	if (!unihan_im_module_get_info)
		return 0;

	ret = (*unihan_im_module_get_info)(args);
	if (ret == 0)
		return 0;

	mod->id = (char *)args[0].value;
	mod->name = (char *)args[1].value;
	mod->version = (char *)args[2].value;

	IMInfo *iminfo = (IMInfo *)args[3].value;
	if (!iminfo) return 0;
	while (iminfo) {
		IMInfo *foo;
		IMList *i = (IMList *)malloc(sizeof(IMList));
		memset(i, 0, sizeof(IMList));
		i->imModule = mod;
		i->num = d->numIM++;
		i->id = iminfo->id;
		i->name = iminfo->name;
		i->version = iminfo->version;
		i->imUserData = iminfo->imUserData;
		i->m = iminfo->methodTable;
#if 0		
		i->imPropertyList = iminfo->proplist;
#endif		
		i->initialized = 0;

		if (d->inputMethods) d->inputMethods->prev = i;
		i->next = d->inputMethods;
		d->inputMethods = i;
		d->inputMethods->prev = NULL;

		unihan_debug("im_module_get_info(): found IM %s/%s", mod->id, i->id);

		foo = iminfo;
		iminfo = iminfo->next;
		num_im++;
		free(foo);
	}
	return num_im;
}


/****************************************************************************/
/**
 * @brief Sets Traditional/Simplified Chinese conversion mode
 * for current session
 *
 * @param s     current IMLSession
 * @param tcsc  the TC/SC conversion mode
 */
void
tcsc_set(iml_session_t *s, UnihanTCSCMode tcsc)
{
        DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;

        if (dd->tcsc != tcsc) {
                int int_values[2] = {
                        UNIHAN_AUX_STATUS_TCSC_MODE,
                        tcsc
                };
                dd->tcsc = tcsc;
                aux_draw(s, AUX_STATUS_CLASS_NAME, 2, int_values, 0, NULL);

		tooltip_update_tcsc(s);
        }
}

#if NEW_PROPERTY

/****************************************************************************/
char *
im_get_curr_im_id(iml_session_t *s)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	return ((IMList*)scdata->currIM)->id;
}

/****************************************************************************/
void toggle_property(iml_session_t *s, char* key)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	im_propagate_property_toggled_event(s, (IMList*)scdata->currIM, key);
}

/****************************************************************************/
void register_im_property(iml_session_t *s, char* imid)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	im_propagate_property_register_event(s, (IMList*)scdata->currIM, imid);
}

/****************************************************************************/
void
destroy_im_properties(UnihanPropertyList *prop_list)
{
	int i;
	
	for(i = 0; i<prop_list->propertiesNum; i++) {
		
		if(prop_list->properties[i].key)
			free(prop_list->properties[i].key);

		if(prop_list->properties[i].label)
			free(prop_list->properties[i].label);
			
		if(prop_list->properties[i].icon)
			free(prop_list->properties[i].icon);
				
		if(prop_list->properties[i].tip)
			free(prop_list->properties[i].tip);
				
	}
	
	free(prop_list->properties);
	free(prop_list);

}

int convert_props_to_string(iml_session_t *s,
							UnihanPropertyList *prop_list,
							int int_values[],
							UTFCHAR* string_values[])
{
	char *buf;
	int i;
	
	for(i = 0; i<prop_list->propertiesNum; i++)
	{
		buf = (char*)malloc(sizeof(char)*256);
		memset(buf, 0, sizeof(char)*256);
		
		strcpy(buf, prop_list->properties[i].key);
		strcat(buf,"|");
		
		if(prop_list->properties[i].label)
			strcat(buf, prop_list->properties[i].label);
		else
			strcat(buf, "null");
		strcat(buf,"|");
		
		if(prop_list->properties[i].icon)
			strcat(buf, prop_list->properties[i].icon);
		else
			strcat(buf, "null");
		strcat(buf,"|");
		
		if(prop_list->properties[i].tip)
			strcat(buf, prop_list->properties[i].tip);
		else
			strcat(buf, "null");
			
		int_values[i+1] = prop_list->properties[i].state;
			
		string_values[i+1] = utf8_to_utf16be(s, buf, NULL);
	}

	return i+1;	

}


#else

/****************************************************************************/
void
check_module_info(iml_session_t *s)
{
	DataPerDesktop *dtdata = (DataPerDesktop *)(s->desktop->specific_data);
	DataPerSession *scdata = (DataPerSession *)(s->specific_data); 
	IMManagerData *d = (IMManagerData *)(dtdata->im_manager_data);
	DIR *dir;
	union {
		struct dirent d;
		char b[sizeof(struct dirent) + NAME_MAX + 1];
	} u;
	struct dirent *res = NULL;
	int numIM = 0;

	int find_unispim_module = 0;
	int check_result = 0;
	char *so_name = NULL;

	dir = opendir(IM_MODULES_DIR);
	if (!dir)
		return;

	IMModuleList *new = NULL;
	const char *dlerrmsg = NULL;
	while (readdir_r(dir, &u.d, &res) == 0 && res != NULL) {
		if (res->d_name && !strcmp("upim.so", res->d_name)) {
			so_name = strdup(res->d_name); 
			find_unispim_module = 1;
			continue;
		}
	}
	if (find_unispim_module) {
		if (!has_add_upim) {
			new = (IMModuleList*)malloc(sizeof(IMModuleList));
			if (!new)
				return;
			memset(new, 0, sizeof(IMModuleList));
			new->fullpath = (char*)malloc(strlen(IM_MODULES_DIR)
				+ strlen(so_name) + 2);
			if (!new->fullpath) {
				free(new);
				return; 	//why don't continue????
				//continue;
			}

			strcpy(new->fullpath, IM_MODULES_DIR "/");
			strcat(new->fullpath, so_name);
			new->dlh = dlopen(new->fullpath, RTLD_LAZY);
			if (!new->dlh)
				return;

			new->init = dlsym(new->dlh, "unihan_im_module_init");
			dlerrmsg = dlerror();
			if (dlerrmsg != NULL)
				return;
			new->done = dlsym(new->dlh, "unihan_im_module_done");
			dlerrmsg = dlerror();
			if (dlerrmsg != NULL)
				return;

			if (new->init)
				if ((*new->init)() == 0)
					return;

			if (im_module_get_info(d, new) == 0) {
				if (new->done)
					(*new->done)();
				return;
			}

			if (d->imModules) {
				d->imModules->prev = new;
			}
			new->next = d->imModules;
			d->imModules = new;
			d->imModules->prev = NULL;
			numIM = im_manager_get_num_im(s->desktop);
			dtdata->imConfig = (IMConfigList **)realloc(dtdata->imConfig, sizeof(IMConfigList *)*numIM);
			scdata->userData = (UserDataTable *)realloc(scdata->userData, sizeof(UserDataTable)*numIM);
			if (!dtdata->imConfig || !scdata->userData) {
				return;
			}
			memset(&dtdata->imConfig[numIM-1], 0, sizeof(IMConfigList *));
			memset(&scdata->userData[numIM-1], 0, sizeof(UserDataTable));
			has_add_upim = 1;
			check_result = 2;
		} else {
			check_result = 1;
		}
	}else {
		if (has_add_upim) {
			check_result = 0;	
		} else {
			check_result = 1;
		}
	}

	closedir(dir);
    int int_values[2];
    UTFCHAR **string_values;
    IMList *im = d->inputMethods;
    int i;

	if (check_result) {
    	int_values[0] = UNIHAN_ENGINE_GET_IM_LIST;
    	i = d->numIM;
    	string_values = (UTFCHAR**)s->If->m->iml_new(s, sizeof(UTFCHAR*)*i*2);
    	memset(string_values, 0, sizeof(UTFCHAR*) * i * 2);
    	for (i = 0; im; i++, im = im->next) {
			string_values[i*2] = utf8_to_utf16be(s, (UTF8CHAR *)im->id, NULL);
			string_values[i*2+1] = utf8_to_utf16be(s, (UTF8CHAR *)im->name, NULL);
		}
		aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, d->numIM * 2, string_values);
	} else if (check_result == 0) {
		int_values[0] = UNIHAN_ENGINE_GET_IM_LIST;
		i = d->numIM - 1;
		string_values = (UTFCHAR**)s->If->m->iml_new(s, sizeof(UTFCHAR*)*i*2);
    	memset(string_values, 0, sizeof(UTFCHAR*) * i * 2);
		i = 0;
		while (im) {
			if (!strcmp(im->id, "upim")) {
				im = im->next;
				continue;
			}
			string_values[i*2] = utf8_to_utf16be(s, (UTF8CHAR *)im->id, NULL);
			string_values[i*2+1] = utf8_to_utf16be(s, (UTF8CHAR *)im->name, NULL);
			i++;
			im = im->next;
		}
		aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, (d->numIM -1) * 2, string_values);
	}
}


/**
 * @brief Toggles halfwidth/fullwidth mode
 *
 * @param s     current IMLSession
 */
void
toggle_width(iml_session_t *s)
{
	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	IMManagerData *d = (IMManagerData *)dd->im_manager_data;
	IMConfigList *imConfig = NULL;
	IMList *im = d->inputMethods;
	int int_values[1];
	char *key = "width";
	char *value;

	im = (IMList *)ds->currIM;
	if (!im)
		return;

	ds->fullwidth ^= 0x01;

	if (ds->fullwidth) {
		int_values[0] = UNIHAN_AUX_STATUS_FULLWIDTH;
		value = "true";
	} else {
		int_values[0] = UNIHAN_AUX_STATUS_HALFWIDTH;
		value = "false";
	}

	imConfig = dd->imConfig[im->num];
	while (imConfig) {
		if (!strcasecmp(key, imConfig->key))
			break;
	imConfig = imConfig->next;
	}
	if (imConfig) {
		if (imConfig->value)
			free(imConfig->value);
		imConfig->value = strdup(value);
	} else {
		imConfig = (IMConfigList *)malloc(sizeof(IMConfigList));
		imConfig->key = strdup(key);
		imConfig->value = strdup(value);
		imConfig->next = dd->imConfig[im->num];
		dd->imConfig[im->num] = imConfig;
	}
	im_propagate_config_change_event(s, im, key);

	aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);

	tooltip_update_width(s, ds->fullwidth);
}


/**
 * @brief Updates Aux of current halfwidth/fullwidth mode
 *
 * @param s     current IMLSession
 */
void
check_fullwidth(iml_session_t *s)
{
	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
        DataPerSession *ds = (DataPerSession *)s->specific_data;
	IMManagerData *d = (IMManagerData *)dd->im_manager_data;
        IMConfigList *imConfig = NULL;
      	IMList *im = d->inputMethods;
        int int_values[1];
	char *key = "width";
	char *value = "true";

	im = (IMList *)ds->currIM;
	if (!im) {	/* English */
                int_values[0] = UNIHAN_AUX_STATUS_HALFWIDTH;

	        aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);
                return;
	}

        imConfig = dd->imConfig[im->num];
        while (imConfig) {
                if (!strcasecmp(key, imConfig->key))
                        break;
                imConfig = imConfig->next;
        }
        if (imConfig) {
                if (imConfig->value)
                        free(imConfig->value);
                imConfig->value = strdup(value);
        } else {
                imConfig = (IMConfigList *)malloc(sizeof(IMConfigList));
                imConfig->key = strdup(key);
                imConfig->value = strdup(value);
                imConfig->next = dd->imConfig[im->num];
                dd->imConfig[im->num] = imConfig;
        }
		//return;
        im_propagate_config_change_event(s, im, key);

        if (ds->fullwidth == 1)
                int_values[0] = UNIHAN_AUX_STATUS_FULLWIDTH;
        else
                int_values[0] = UNIHAN_AUX_STATUS_HALFWIDTH;

        aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);
}


/****************************************************************************/
/**
 * @brief Toggles Chinese/English punctuation mode
 *
 * @param s     current IMLSession
 */
void
toggle_puncce(iml_session_t *s)
{
	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	IMManagerData *d = (IMManagerData *)dd->im_manager_data;
        IMConfigList *imConfig = NULL;
      	IMList *im = d->inputMethods;
	int int_values[1];
	char *key = "puncce";
	char *value;

	ds->puncce ^=0x01;

	if (ds->puncce == 0)
	{
		int_values[0] = UNIHAN_AUX_STATUS_ENGLISHPUNC;
		value = "false";
	} else {
		int_values[0] = UNIHAN_AUX_STATUS_CHINESEPUNC;
		value = "true";
	}


	im = (IMList *)ds->currIM;
	if (!im)
                return;

        imConfig = dd->imConfig[im->num];
        while (imConfig) {
                if (!strcasecmp(key, imConfig->key))
                        break;
                imConfig = imConfig->next;
        }
        if (imConfig) {
                if (imConfig->value)
                        free(imConfig->value);
                imConfig->value = strdup(value);
        } else {
                imConfig = (IMConfigList *)malloc(sizeof(IMConfigList));
                imConfig->key = strdup(key);
                imConfig->value = strdup(value);
                imConfig->next = dd->imConfig[im->num];
                dd->imConfig[im->num] = imConfig;
        }
        im_propagate_config_change_event(s, im, key);
	aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);

	tooltip_update_puncce(s, ds->puncce);
}

/**
 * @brief Updates Aux of current Chinese/English punctuation mode
 *
 * @param s     current IMLSession
 */
void
check_puncce(iml_session_t *s)
{
	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	IMManagerData *d = (IMManagerData *)dd->im_manager_data;
        IMConfigList *imConfig = NULL;
      	IMList *im = d->inputMethods;
	int int_values[1];
	char *key = "puncce";
	char *value = "true";
	
	im = (IMList *)ds->currIM;
	if (!im) {
		int_values[0] = UNIHAN_AUX_STATUS_ENGLISHPUNC;
		aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);
                return;
	}

	if (ds->puncce == 0)
		int_values[0] = UNIHAN_AUX_STATUS_ENGLISHPUNC;
	else
		int_values[0] = UNIHAN_AUX_STATUS_CHINESEPUNC;

        imConfig = dd->imConfig[im->num];
        while (imConfig) {
                if (!strcasecmp(key, imConfig->key))
                        break;
                imConfig = imConfig->next;
        }
        if (imConfig) {
                if (imConfig->value)
                        free(imConfig->value);
                imConfig->value = strdup(value);
        } else {
                imConfig = (IMConfigList *)malloc(sizeof(IMConfigList));
                imConfig->key = strdup(key);
                imConfig->value = strdup(value);
                imConfig->next = dd->imConfig[im->num];
                dd->imConfig[im->num] = imConfig;
        }
        im_propagate_config_change_event(s, im, key);
	aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);
}
#endif

/****************************************************************************/
IMConfigList *
im_config_get(iml_session_t *s, char *imid)
{
        DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dd->im_manager_data;
      	IMList *im = d->inputMethods;

        while (im) {
		if (!strcasecmp(imid, im->id))
			break;
		im = im->next;
	}
	if (!im)
                return NULL;

        return dd->imConfig[im->num];
}


/****************************************************************************/
void
im_config_change(iml_session_t *s, char *imid, char *key, char *value)
{
	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dd->im_manager_data;
        IMConfigList *imConfig = NULL;
      	IMList *im = d->inputMethods;

	while (im) {
		if (!strcasecmp(imid, im->id))
			break;
		im = im->next;
	}
	if (!im)
                return;

        imConfig = dd->imConfig[im->num];
        while (imConfig) {
                if (!strcasecmp(key, imConfig->key))
                        break;
                imConfig = imConfig->next;
        }
        if (imConfig) {
                if (imConfig->value)
                        free(imConfig->value);
                imConfig->value = value;
        } else {
                imConfig = (IMConfigList *)malloc(sizeof(IMConfigList));
                imConfig->key = key;
                imConfig->value = value;
                imConfig->next = dd->imConfig[im->num];
                dd->imConfig[im->num] = imConfig;
        }
        im_propagate_config_change_event(s, im, key);
}


/****************************************************************************/
static int
im_propagate_config_change_event(iml_session_t *s, IMList *im, char *key)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;

	// ensure IM is init'd for desktop and session
	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
        init_uim(&uim, im, s);

		IMConfigChangeEvent imccev;
		imccev.type = IM_CONFIG_CHANGE;
		imccev.key = key;
		imccev.value = 0;

		return im->m->handle_event(&uim, (IMEvent *)&imccev);
	}
	return False;
}

#if 0

/****************************************************************************/
static int
im_propagate_property_change_event(iml_session_t *s, IMList *im, char *key, int value)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;

	// ensure IM is init'd for desktop and session
	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
		IMConfigChangeEvent imccev;
		
		uim.privateData = s;
		uim.id = im->id;
		uim.host_name = s->desktop->host_name;
		uim.user_name = s->desktop->user_name;
		uim.imUserData = im->imUserData;
		uim.desktopUserData = im->userData;
		uim.sessionUserData = scdata->userData[im->num].data;
		uim.m = &d->unihanMethodsTable;

		imccev.type = IM_CONFIG_CHANGE;
		imccev.key = key;
		imccev.value = value;

		return im->m->handle_event(&uim, (IMEvent *)&imccev);
	}

	return False;
}
#endif

#if NEW_PROPERTY
static int
im_propagate_property_toggled_event(iml_session_t *s, IMList *im, char *key)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;

	// ensure IM is init'd for desktop and session
	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
        init_uim(&uim, im, s);

		IMPropertyToggledEvent imptev;
		imptev.type = IM_PROPERTY_TOGGLED;
		imptev.key = key;
		
		return im->m->handle_event(&uim, (IMEvent *)&imptev);
	}
	return False;
}

static int
im_propagate_property_register_event(iml_session_t *s, IMList *im, char *imid)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;

	// ensure IM is init'd for desktop and session
	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
        init_uim(&uim, im, s);

		IMPropertyRegisterEvent imprev;
		imprev.type = IM_PROPERTY_REGISTER;
		imprev.imid = imid;
		
		return im->m->handle_event(&uim, (IMEvent *)&imprev);
	}
	return False;
}
#endif

/****************************************************************************/
void
toggle_punctuation_keyboard(iml_session_t *s)
{
 	DataPerSession *scdata = (DataPerSession *)s->specific_data;

#if 0
	UnihanPropertyArgList prop;

	prop = im_get_runtime_property(s, UNIHAN_RUNTIME_PROPERTY_SOFT_KB, UNIHAN_SOFTKB_SYMBOL);

	if(prop->type) {
		if(prop->UnihanProperty.rt_property.value) {
			prop->UnihanProperty.rt_property.value = 0;
			scdata->punctuation = 0;
			punc_keyboard_hide(s);
		} else {
			prop->UnihanProperty.rt_property.value = 1;
			scdata->punctuation = 1;
			punc_keyboard_show(s);
		}
	}
#endif
	
#if NEW_PROPERTY
#else
        scdata->punctuation ^= 0x01;

        if (scdata->punctuation) {
                punc_keyboard_show(s);
        } else {
                punc_keyboard_hide(s);
        }
#endif
}

void
toggle_us_keyboard(iml_session_t *s)
{
 	DataPerSession *scdata = (DataPerSession *)s->specific_data;
#if 0
	UnihanPropertyArgList prop;

	prop = im_get_runtime_property(s, UNIHAN_RUNTIME_PROPERTY_SOFT_KB, UNIHAN_SOFTKB_US);

	if(prop->type) {
		if(prop->UnihanProperty.rt_property.value) {
			prop->UnihanProperty.rt_property.value = 0;
			scdata->us_kbd = 0;
			us_keyboard_hide(s);
		} else {
			prop->UnihanProperty.rt_property.value = 1;
			scdata->us_kbd = 1;
			us_keyboard_show(s);
		}
	}
#endif
	
#if NEW_PROPERTY
#else
        scdata->us_kbd ^= 0x01;

        if (scdata->us_kbd) {
                us_keyboard_show(s);
        } else {
                us_keyboard_hide(s);
        }
#endif
}

#if 0
void
im_set_symbol_user_table(iml_session_t *s, int numSut, SymbolUserTable *sut)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im = d->inputMethods;

	while (im) {
		if (!strcasecmp("pinyin", im->id))
			break;
		im = im->next;
	}
	if (!im)
		return;

	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
		IMUserTableEvent imUTEv;

		uim.privateData = s;
		uim.id = im->id;
		uim.host_name = s->desktop->host_name;
		uim.user_name = s->desktop->user_name;
		uim.imUserData = im->imUserData;
		uim.desktopUserData = im->userData;
		uim.sessionUserData = scdata->userData[im->num].data;
		uim.m = &d->unihanMethodsTable;

		imUTEv.type = IM_SYMBOL_USER_TABLE;
		imUTEv.table = (void *)sut;
		imUTEv.numItem = numSut;

		im->m->handle_event(&uim, (IMEvent *)&imUTEv);
	}
}

void
im_set_word_user_table(iml_session_t *s, int numWut, WordUserTable *wut)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im = d->inputMethods;

	while (im) {
		if (!strcasecmp("pinyin", im->id))
			break;
		im = im->next;
	}
	if (!im)
		return;

	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
		IMUserTableEvent imUTEv;

		uim.privateData = s;
		uim.id = im->id;
		uim.imUserData = im->imUserData;
		uim.desktopUserData = im->userData;
		uim.sessionUserData = scdata->userData[im->num].data;
		uim.m = &d->unihanMethodsTable;

		imUTEv.type = IM_WORD_USER_TABLE;
		imUTEv.table = (void *)wut;
		imUTEv.numItem = numWut;

		im->m->handle_event(&uim, (IMEvent *)&imUTEv);
	}
}
#endif

void im_config_unispim(iml_session_t *s,int count_values,int unispim_values[])
{
	FILE *config_fd;
		
	if((config_fd = fopen("/usr/local/unispim/unispim.conf","wt"))== NULL) {
		unihan_error("Open unispim config file Error!");
		//exit(1); 
        return;
	}

	if(count_values<30) {
		unihan_error("unispim properties not enough!");
		//exit(1); 
        return;
	}

	// Unispim expects its config file in GB2312 encoding
	//
	// fprintf(config_fd,"输入风格=%d\n",unispim_values[0]);
	fprintf(config_fd,"\xCA\xE4\xC8\xEB\xB7\xE7\xB8\xF1=%d\n",unispim_values[0]);
	// fprintf(config_fd,"用字母键选择候选字词=%d\n",unispim_values[1]);
	fprintf(config_fd,"\xD3\xC3\xD7\xD6\xC4\xB8\xBC\xFC\xD1\xA1\xD4\xF1\xBA\xF2\xD1\xA1\xD7\xD6\xB4\xCA=%d\n",unispim_values[1]);
	// fprintf(config_fd,"使用双拼输入=%d\n",unispim_values[2]);
	fprintf(config_fd,"\xCA\xB9\xD3\xC3\xCB\xAB\xC6\xB4\xCA\xE4\xC8\xEB=%d\n",unispim_values[2]);
	// fprintf(config_fd,"双拼输入时显示提示信息=%d\n",unispim_values[3]);
	fprintf(config_fd,"\xCB\xAB\xC6\xB4\xCA\xE4\xC8\xEB\xCA\xB1\xCF\xD4\xCA\xBE\xCC\xE1\xCA\xBE\xD0\xC5\xCF\xA2=%d\n",unispim_values[3]);
	// fprintf(config_fd,"光标跟随=%d\n",unispim_values[4]);
	fprintf(config_fd,"\xB9\xE2\xB1\xEA\xB8\xFA\xCB\xE6=%d\n",unispim_values[4]);
	// fprintf(config_fd,"动态调整词频=%d\n",unispim_values[5]);
	fprintf(config_fd,"\xB6\xAF\xCC\xAC\xB5\xF7\xD5\xFB\xB4\xCA\xC6\xB5=%d\n",unispim_values[5]);
	// fprintf(config_fd,"自动隐藏低频词=%d\n",unispim_values[6]);
	fprintf(config_fd,"\xD7\xD4\xB6\xAF\xD2\xFE\xB2\xD8\xB5\xCD\xC6\xB5\xB4\xCA=%d\n",unispim_values[6]);
	// fprintf(config_fd,"动态调整字频=%d\n",unispim_values[7]);
	fprintf(config_fd,"\xB6\xAF\xCC\xAC\xB5\xF7\xD5\xFB\xD7\xD6\xC6\xB5=%d\n",unispim_values[7]);
	// fprintf(config_fd,"最近输入字优先=%d\n",unispim_values[8]);
	fprintf(config_fd,"\xD7\xEE\xBD\xFC\xCA\xE4\xC8\xEB\xD7\xD6\xD3\xC5\xCF\xC8=%d\n",unispim_values[8]);
	// fprintf(config_fd,"每页最多候选个数=%d\n",unispim_values[9]);
	fprintf(config_fd,"\xC3\xBF\xD2\xB3\xD7\xEE\xB6\xE0\xBA\xF2\xD1\xA1\xB8\xF6\xCA\xFD=%d\n",unispim_values[9]);
	// fprintf(config_fd,"中英文输入切换键=%d\n",unispim_values[10]);
	fprintf(config_fd,"\xD6\xD0\xD3\xA2\xCE\xC4\xCA\xE4\xC8\xEB\xC7\xD0\xBB\xBB\xBC\xFC=%d\n",unispim_values[10]);
	// fprintf(config_fd,"回车键定义=%d\n",unispim_values[11]);
	fprintf(config_fd,"\xBB\xD8\xB3\xB5\xBC\xFC\xB6\xA8\xD2\xE5=%d\n",unispim_values[11]);
	// fprintf(config_fd,"多个候选翻页键=%d\n",unispim_values[12]);
	fprintf(config_fd,"\xB6\xE0\xB8\xF6\xBA\xF2\xD1\xA1\xB7\xAD\xD2\xB3\xBC\xFC=%d\n",unispim_values[12]);
	// fprintf(config_fd,"输入第二第三候选字词键=%d\n",unispim_values[13]);
	fprintf(config_fd,"\xCA\xE4\xC8\xEB\xB5\xDA\xB6\xFE\xB5\xDA\xC8\xFD\xBA\xF2\xD1\xA1\xD7\xD6\xB4\xCA\xBC\xFC=%d\n",unispim_values[13]);
	// fprintf(config_fd,"以词定字选字键=%d\n",unispim_values[14]);
	fprintf(config_fd,"\xD2\xD4\xB4\xCA\xB6\xA8\xD7\xD6\xD1\xA1\xD7\xD6\xBC\xFC=%d\n",unispim_values[14]);
	// fprintf(config_fd,"软键盘开关键=%c\n",unispim_values[15]);
	fprintf(config_fd,"\xC8\xED\xBC\xFC\xC5\xCC\xBF\xAA\xB9\xD8\xBC\xFC=%c\n",unispim_values[15]);
	// fprintf(config_fd,"支持GBK大字符集=%d\n",unispim_values[16]);
	fprintf(config_fd,"\xD6\xA7\xB3\xD6GBK\xB4\xF3\xD7\xD6\xB7\xFB\xBC\xAF=%d\n",unispim_values[16]);
	// fprintf(config_fd,"输入简体汉字=%d\n",unispim_values[17]);
	fprintf(config_fd,"\xCA\xE4\xC8\xEB\xBC\xF2\xCC\xE5\xBA\xBA\xD7\xD6=%d\n",unispim_values[17]);
	// fprintf(config_fd,"智能组词=%d\n",unispim_values[18]);
	fprintf(config_fd,"\xD6\xC7\xC4\xDC\xD7\xE9\xB4\xCA=%d\n",unispim_values[18]);
	// fprintf(config_fd,"智能组词自动记忆=%d\n",unispim_values[19]);
	fprintf(config_fd,"\xD6\xC7\xC4\xDC\xD7\xE9\xB4\xCA\xD7\xD4\xB6\xAF\xBC\xC7\xD2\xE4=%d\n",unispim_values[19]);
	// fprintf(config_fd,"按前次输入动态调整字序=%d\n",unispim_values[20]);
	fprintf(config_fd,"\xB0\xB4\xC7\xB0\xB4\xCE\xCA\xE4\xC8\xEB\xB6\xAF\xCC\xAC\xB5\xF7\xD5\xFB\xD7\xD6\xD0\xF2=%d\n",unispim_values[20]);
	// fprintf(config_fd,"数字后跟随英文符号=%d\n",unispim_values[21]);
	fprintf(config_fd,"\xCA\xFD\xD7\xD6\xBA\xF3\xB8\xFA\xCB\xE6\xD3\xA2\xCE\xC4\xB7\xFB\xBA\xC5=%d\n",unispim_values[21]);
	// fprintf(config_fd,"记忆并可输入最近输入过的字词=%d\n",unispim_values[22]);
	fprintf(config_fd,"\xBC\xC7\xD2\xE4\xB2\xA2\xBF\xC9\xCA\xE4\xC8\xEB\xD7\xEE\xBD\xFC\xCA\xE4\xC8\xEB\xB9\xFD\xB5\xC4\xD7\xD6\xB4\xCA=%d\n",unispim_values[22]);
	// fprintf(config_fd,"声母模糊音设置=%d\n",unispim_values[23]);
	fprintf(config_fd,"\xC9\xF9\xC4\xB8\xC4\xA3\xBA\xFD\xD2\xF4\xC9\xE8\xD6\xC3=%d\n",unispim_values[23]);
	// fprintf(config_fd,"韵母模糊音设置=%d\n",unispim_values[24]);
	fprintf(config_fd,"\xD4\xCF\xC4\xB8\xC4\xA3\xBA\xFD\xD2\xF4\xC9\xE8\xD6\xC3=%d\n",unispim_values[24]);
	// fprintf(config_fd,"软键盘索引=%d\n",unispim_values[25]);
	fprintf(config_fd,"\xC8\xED\xBC\xFC\xC5\xCC\xCB\xF7\xD2\xFD=%d\n",unispim_values[25]);
	// fprintf(config_fd,"中文符号=%d\n",unispim_values[26]);
	fprintf(config_fd,"\xD6\xD0\xCE\xC4\xB7\xFB\xBA\xC5=%d\n",unispim_values[26]);
	// fprintf(config_fd,"打开时默认为英文输入状态=%d\n",unispim_values[27]);
	fprintf(config_fd,"\xB4\xF2\xBF\xAA\xCA\xB1\xC4\xAC\xC8\xCF\xCE\xAA\xD3\xA2\xCE\xC4\xCA\xE4\xC8\xEB\xD7\xB4\xCC\xAC=%d\n",unispim_values[27]);
	// fprintf(config_fd,"v状态可输入空格=%d\n",unispim_values[28]);
	fprintf(config_fd,"v\xD7\xB4\xCC\xAC\xBF\xC9\xCA\xE4\xC8\xEB\xBF\xD5\xB8\xF1=%d\n",unispim_values[28]);
	// fprintf(config_fd,"一次输入汉字最多个数=%d\n",unispim_values[29]);
	fprintf(config_fd,"\xD2\xBB\xB4\xCE\xCA\xE4\xC8\xEB\xBA\xBA\xD7\xD6\xD7\xEE\xB6\xE0\xB8\xF6\xCA\xFD=%d\n",unispim_values[29]);

	fclose(config_fd);

	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dd->im_manager_data;
	IMList *im = d->inputMethods;

	while (im) {
		if (!strcasecmp(im->id, "upim"))
			break;
		im = im->next;
	}
	if (!im)
		return;	

	if(unispim_values[11])
		im_propagate_config_change_event(s,im,"reset&enter=1");
	else
		im_propagate_config_change_event(s,im,"reset&enter=0");

	if(count_values == 31) {
		system("rm -f /usr/local/unispim/unispimsp.ini");
		if(unispim_values[30] == 2) {
			system("cp /usr/local/unispim/unispimsp3.ini \
					/usr/local/unispim/unispimsp.ini");
		} else if(unispim_values[30] == 1) {
			system("cp /usr/local/unispim/unispimsp2.ini \
					/usr/local/unispim/unispimsp.ini");
		} else {
			system("cp /usr/local/unispim/unispimsp1.ini \
					/usr/local/unispim/unispimsp.ini");
		}
		system("rm -f /usr/local/unispim/unispimsp.ksc");
		im_notify_switch_event(s,0);
		system("upimd stop");
		system("upimd start");
	}
	im_notify_switch_event(s, 1);
	return;
}	

#if 0

/****************************************************************************/
UnihanPropertyArgList
im_get_current_im_props(iml_session_t *s)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;

	if(!ds->currIMprop_list)
		ds->currIMprop_list = properties_dup(((IMList*)ds->currIM)->imPropertyList);

	return ds->currIMprop_list;
}

/****************************************************************************/
UnihanPropertyArgList
im_get_runtime_property(iml_session_t *s, UnihanRuntimePropertyType type, int name)
{
	UnihanPropertyArgList prop_list;

	prop_list = im_get_current_im_props(s);

	while(prop_list->type) {
		if(prop_list->type == UNIHAN_PROPERTY_RUNTIME &&
				prop_list->UnihanProperty.rt_property.rt_type
				== type
				&& prop_list->UnihanProperty.rt_property.name.nr_name
				== name)
			break;
		prop_list = prop_list->next;
	}
	return prop_list;
}

/******************************************************************/
void im_update_property_list(iml_session_t *s, UnihanPropertyArgList plist)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	UnihanPropertyArgList prop_list;

	if(!ds->currIMprop_list)
		return;
 

    	if(plist->type == UNIHAN_PROPERTY_RUNTIME) {
    		prop_list = ds->currIMprop_list;
    		while(prop_list) {
    			if(prop_list->type == UNIHAN_PROPERTY_RUNTIME
    					&& prop_list->UnihanProperty.rt_property.rt_type
					== plist->UnihanProperty.rt_property.rt_type) {

    				prop_list->UnihanProperty.rt_property.value =
						plist->UnihanProperty.rt_property.value;
    				break;
    			}
    			prop_list = prop_list->next;
	   	}
    	}
	destroy_im_property(plist);
}

/****************************************************************************/
void
im_set_property_list(iml_session_t *s, UnihanPropertyArgList prop_list)
{
	//UnihanPropertyArgList prop_list = pp;

	DataPerSession *ds = (DataPerSession *)s->specific_data;
	DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;

	while(prop_list->type) {
		if(prop_list->type == UNIHAN_PROPERTY_RUNTIME) {
			switch (prop_list->UnihanProperty.rt_property.rt_type) {
			case UNIHAN_RUNTIME_PROPERTY_FULL_HALF:
				if(prop_list->UnihanProperty.rt_property.value) {
					fprintf(stderr, "UNIHAN LE : Invoke im_set_width \
							in im_set_property_list().\n\r");
					im_set_width(s, 1);
					//status_update_width(s, 1);
				} else {
					fprintf(stderr, "UNIHAN LE : Invoke im_set_width in \
							im_set_property_list().\n\r");
					im_set_width(s, 0);
					//status_update_width(s, 0);
				}
				break;
			case UNIHAN_RUNTIME_PROPERTY_PUNC_EN_CH:
				if(prop_list->UnihanProperty.rt_property.value) {
					im_set_puncce(s, 1);
					//status_update_puncce(s, 1);
				} else {
					im_set_puncce(s, 0);
					//status_update_puncce(s, 0);
				}
				break;
			case UNIHAN_RUNTIME_PROPERTY_SOFT_KB:
				if(prop_list->UnihanProperty.rt_property.name.nr_name
						== UNIHAN_SOFTKB_US) {
					if(prop_list->UnihanProperty.rt_property.value) {
						us_keyboard_show(s);
						ds->us_kbd = 1;
					} else {
						us_keyboard_hide(s);
						ds->us_kbd = 0;
					}
				} else if (prop_list->UnihanProperty.rt_property.name.nr_name
						== UNIHAN_SOFTKB_SYMBOL) {
					if(prop_list->UnihanProperty.rt_property.value) {
						punc_keyboard_show(s);
						ds->punctuation = 1;
					} else {
						punc_keyboard_hide(s);
						ds->punctuation = 0;
					}
				}
				break;
			case UNIHAN_RUNTIME_PROPERTY_TCSC:
				tcsc_set(s, prop_list->UnihanProperty.rt_property.value);
				break;
			case UNIHAN_RUNTIME_PROPERTY_STATE_EN_CH:
			default:
				break;
			}
		}
		prop_list = prop_list->next;
	}
	
}

/****************************************************************************/
void
destroy_im_property(UnihanPropertyArgList plist)
{
	UnihanPropertyArgList p;
	while(plist->type) {
		p = plist;
		plist = plist->next;
		if(p->type == UNIHAN_PROPERTY_RUNTIME) {
			if(p->UnihanProperty.rt_property.name.name_string)
				free(p->UnihanProperty.rt_property.name.name_string);

			if(p->UnihanProperty.rt_property.name.charset)
				free(p->UnihanProperty.rt_property.name.charset);
				
			if(p->UnihanProperty.rt_property.name.language)
				free(p->UnihanProperty.rt_property.name.language);
				
			if(p->UnihanProperty.rt_property.icon.icon0)
				free(p->UnihanProperty.rt_property.icon.icon0);
				
			if(p->UnihanProperty.rt_property.icon.icon1)
				free(p->UnihanProperty.rt_property.icon.icon1);
		} else if(p->type == UNIHAN_RPOPERTY_USER) {
			free(p->UnihanProperty.u_property.priv_data);
		}
		free(p);
	}
}

/****************************************************************************/
void
im_set_width(iml_session_t *s, int is_full)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	IMList *im; 
	char *key = "width";


	im = (IMList *)ds->currIM;
	if (!im)
		return;

	im_propagate_property_change_event(s, im, key, is_full);
}

/****************************************************************************/
void
status_update_width(iml_session_t *s, int is_full)
{
	/*
	int int_values[2];
	UTFCHAR *string_values[2];
	UnihanPropertyArgList new_property;

	int_values[0] = UNIHAN_AUX_STATUS_GET_PROPS;
	string_values[0] = utf8_to_utf16be(s, "null", NULL);

	new_property = (UnihanPropertyArgList)malloc(sizeof(UnihanPropertyArgs));
	memset(new_property, 0, sizeof(UnihanPropertyArgs));
	new_property->type = UNIHAN_PROPERTY_RUNTIME;
	new_property->UnihanProperty.rt_property.rt_type = UNIHAN_RUNTIME_PROPERTY_FULL_HALF;
	new_property->UnihanProperty.rt_property.value = is_full;
	new_property->UnihanProperty.rt_property.name.name_string = "width";
	new_property->UnihanProperty.rt_property.name.nr_name = 0;
	
	convert_props_to_string(s, new_property, 1, int_values, string_values);
	
	aux_draw(s, AUX_STATUS_CLASS_NAME, 2, int_values, 2, string_values);	
	*/

	int int_values[1];
    
	if(is_full)
		int_values[0] = UNIHAN_AUX_STATUS_FULLWIDTH;
	else
		int_values[0] = UNIHAN_AUX_STATUS_HALFWIDTH;

	aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);

	tooltip_update_width(s, is_full);
}

/****************************************************************************/
void
im_set_puncce(iml_session_t *s, int is_CH)
{
	DataPerSession *ds = (DataPerSession *)s->specific_data;
	IMList *im;
	char *key = "puncce";

	im = (IMList *)ds->currIM;
	if (!im)
                return;


	im_propagate_property_change_event(s, im, key, is_CH);
}

/****************************************************************************/
void
status_update_puncce(iml_session_t *s, int is_CH)
{
	/*
	int int_values[2];
	UTFCHAR *string_values[2];
	UnihanPropertyArgList new_property;

	int_values[0] = UNIHAN_AUX_STATUS_GET_PROPS;
	string_values[0] = utf8_to_utf16be(s, "null", NULL);

	new_property = (UnihanPropertyArgList)malloc(sizeof(UnihanPropertyArgs));
	memset(new_property, 0, sizeof(UnihanPropertyArgs));
	new_property->type = UNIHAN_PROPERTY_RUNTIME;
	new_property->UnihanProperty.rt_property.rt_type = UNIHAN_RUNTIME_PROPERTY_PUNC_EN_CH;
	new_property->UnihanProperty.rt_property.value = is_CH;
	new_property->UnihanProperty.rt_property.name.name_string = "puncce";
	new_property->UnihanProperty.rt_property.name.nr_name = 0;

	convert_props_to_string(s, new_property, 1, int_values, string_values);

	aux_draw(s, AUX_STATUS_CLASS_NAME, 2, int_values, 2, string_values);
	*/

	int int_values[1];

	if(is_CH)
		int_values[0] = UNIHAN_AUX_STATUS_CHINESEPUNC;
	else
		int_values[0] = UNIHAN_AUX_STATUS_ENGLISHPUNC;
 
	aux_draw(s, AUX_STATUS_CLASS_NAME, 1, int_values, 0, NULL);

	tooltip_update_puncce(s, is_CH);
}	


int
convert_props_to_string(iml_session_t *s,
		UnihanPropertyArgList prop_list,
		int num_props,
		int int_values[],
		UTFCHAR* string_values[])
{
	int num_string = 1;
	char *buf;
	int i;
	size_t size;

	while(prop_list->type && num_props) {
		buf = (char*)malloc(sizeof(char)*256);
		memset(buf, 0, sizeof(char)*256);

		if(prop_list->type == UNIHAN_RPOPERTY_USER) {
			int_values[num_string] = UNIHAN_RPOPERTY_USER;

			size = prop_list->UnihanProperty.u_property.size;

			if(size > 100) {
				buf[0] = size / 100;
				buf[1] = size % 100;
			} else {
				buf[0] = 0;
				buf[1] = size;
			}
			if(size)
				memcpy(buf+2, prop_list->UnihanProperty.u_property.priv_data, size);
			for(i = 0; i < size+2; i++)
				if(buf[i] == 0)
					buf[i] = 127;
			string_values[num_string] = utf8_to_utf16be(s, buf, NULL);
			
		} else if(prop_list->type == UNIHAN_PROPERTY_RUNTIME) {
			int_values[num_string] = UNIHAN_PROPERTY_RUNTIME;

			buf[0] = prop_list->UnihanProperty.rt_property.rt_type+'0';
			buf[1] = prop_list->UnihanProperty.rt_property.value+'0';

			if(prop_list->UnihanProperty.rt_property.name.nr_name)
				buf[2] = prop_list->UnihanProperty.rt_property.name.nr_name;
			else
				buf[2] = 127;

			if(prop_list->UnihanProperty.rt_property.name.name_string)
				strcpy(buf+3, prop_list->UnihanProperty.rt_property.name.
						name_string);
			else
				strcpy(buf+3, "null");
			strcat(buf,"|");

			if(prop_list->UnihanProperty.rt_property.name.charset)
				strcat(buf, prop_list->UnihanProperty.rt_property.name.charset);
			else
				strcat(buf, "null");
			strcat(buf,"|");

			if(prop_list->UnihanProperty.rt_property.name.language)
				strcat(buf, prop_list->UnihanProperty.rt_property.name.language);
			else
				strcat(buf, "null");
			strcat(buf,"|");

			if(prop_list->UnihanProperty.rt_property.icon.icon0)
				strcat(buf, prop_list->UnihanProperty.rt_property.icon.icon0);
			else
				strcat(buf, "null");
			strcat(buf,"|");
			
			if(prop_list->UnihanProperty.rt_property.icon.icon1)
				strcat(buf, prop_list->UnihanProperty.rt_property.icon.icon1);
			else
				strcat(buf, "null");
				 
			string_values[num_string] = utf8_to_utf16be(s, buf, NULL);
			
		}
		num_string++;
		num_props--;
		prop_list = prop_list->next;
	}
	return num_string;	
}

int
convert_string_to_props(int int_values,
		char* string_values,
		UnihanPropertyArgList *prop_list)
{
	int i;
	size_t size;
	UnihanPropertyArgList new_property;

	if(string_values == NULL)
		return 0;

	if(*prop_list == NULL) {
		new_property = (UnihanPropertyArgList)malloc(sizeof(UnihanPropertyArgs));
		memset(new_property, 0, sizeof(UnihanPropertyArgs));
		new_property->type = UNIHAN_PROPERTY_NONE;
		*prop_list = new_property;
	}

	new_property = (UnihanPropertyArgList)malloc(sizeof(UnihanPropertyArgs));

	if(int_values == UNIHAN_RPOPERTY_USER) {
		new_property->type = UNIHAN_RPOPERTY_USER;

		if(string_values[0] == 127)
			new_property->UnihanProperty.u_property.size = size
					= string_values[1];
		else
			new_property->UnihanProperty.u_property.size = size
					= string_values[0]*100 + string_values[1];

		for(i = 2; i<size+2; i++) {
			if(string_values[i] == 127)
				string_values[i] = 0;
		}

		new_property->UnihanProperty.u_property.priv_data =
				(void*)malloc(sizeof(char)*size);
		memcpy(new_property->UnihanProperty.u_property.priv_data,
				(void*)(string_values+2), size);
	} else if (int_values == UNIHAN_PROPERTY_RUNTIME) {
		new_property->type = UNIHAN_PROPERTY_RUNTIME;
		new_property->UnihanProperty.rt_property.rt_type = string_values[0]-'0';
		new_property->UnihanProperty.rt_property.value = string_values[1]-'0';

		if(string_values[2] == '0')
			new_property->UnihanProperty.rt_property.name.nr_name = 0;
		else
			new_property->UnihanProperty.rt_property.name.nr_name = string_values[2];

		char *p;
		char *delim = "|";
		p = strtok(string_values + 3, delim);

		if(strcmp(p,"null")!=0) {
			new_property->UnihanProperty.rt_property.name.name_string =
					(char*)malloc(sizeof(char)*(strlen(p)+1));
			strcpy(new_property->UnihanProperty.rt_property.name.name_string, p);
		} else {
			new_property->UnihanProperty.rt_property.name.name_string = NULL;
		}

		p = strtok(NULL , delim);

		if(strcmp(p,"null")!=0) {
			new_property->UnihanProperty.rt_property.name.charset =
					(char*)malloc(sizeof(char)*(strlen(p)+1));
			strcpy(new_property->UnihanProperty.rt_property.name.charset, p);
		} else {
			new_property->UnihanProperty.rt_property.name.charset = NULL;
		}

		p = strtok(NULL , delim);

		if(strcmp(p,"null")!=0) {
			new_property->UnihanProperty.rt_property.name.language =
					(char*)malloc(sizeof(char)*(strlen(p)+1));
			strcpy(new_property->UnihanProperty.rt_property.name.language, p);
		} else {
			new_property->UnihanProperty.rt_property.name.language = NULL;
		}

		p = strtok(NULL , delim);

		if(strcmp(p,"null")!=0) {
			new_property->UnihanProperty.rt_property.icon.icon0 =
					(char*)malloc(sizeof(char)*(strlen(p)+1));
			strcpy(new_property->UnihanProperty.rt_property.icon.icon0, p);
		} else {
			new_property->UnihanProperty.rt_property.icon.icon0 = NULL;
		}

		p = strtok(NULL , delim);

		if(strcmp(p,"null")!=0) {
			new_property->UnihanProperty.rt_property.icon.icon1 =
					(char*)malloc(sizeof(char)*(strlen(p)+1));
			strcpy(new_property->UnihanProperty.rt_property.icon.icon1, p);
		} else {
			new_property->UnihanProperty.rt_property.icon.icon1 = NULL;
		}
	}

	new_property->next = NULL;
	
	if(*prop_list) {
		new_property->next = *prop_list;
	} else {
		new_property->next = NULL;
	}

	*prop_list = new_property;
	return 1;
}

/****************************************************************************/
UnihanPropertyArgList
properties_dup(UnihanPropertyArgList prop_list)
{
	UnihanPropertyArgList new_property;
	UnihanPropertyArgList newlist = NULL;

	newlist = (UnihanPropertyArgList)malloc(sizeof(UnihanPropertyArgs));
	memset(newlist, 0, sizeof(UnihanPropertyArgs));
	newlist->type = UNIHAN_PROPERTY_NONE;
	
	if(prop_list->type == UNIHAN_PROPERTY_NONE) {
		return newlist;
	}

	while(prop_list->type) {
		new_property = (UnihanPropertyArgList)malloc(sizeof(UnihanPropertyArgs));
		memset(new_property, 0, sizeof(UnihanPropertyArgs));
		if(prop_list->type == UNIHAN_PROPERTY_RUNTIME) {
			new_property->type = prop_list->type;
			new_property->UnihanProperty.rt_property.rt_type
					= prop_list->UnihanProperty.rt_property.rt_type;
			new_property->UnihanProperty.rt_property.value
					= prop_list->UnihanProperty.rt_property.value;
			if(prop_list->UnihanProperty.rt_property.name.name_string)
				new_property->UnihanProperty.rt_property.name.name_string
						= strdup(prop_list->UnihanProperty.
						rt_property.
						name.
						name_string);
			if(prop_list->UnihanProperty.rt_property.name.charset)
				new_property->UnihanProperty.rt_property.name.charset
						= strdup(prop_list->UnihanProperty.
						rt_property.
						name.
						charset);
			if(prop_list->UnihanProperty.rt_property.name.language)
				new_property->UnihanProperty.rt_property.name.language
						= strdup(prop_list->UnihanProperty.
						rt_property.
						name.
						language);
			new_property->UnihanProperty.rt_property.name.nr_name
					= prop_list->UnihanProperty.rt_property.name.nr_name;
			if(prop_list->UnihanProperty.rt_property.icon.icon0)
				new_property->UnihanProperty.rt_property.icon.icon0
						= strdup(prop_list->UnihanProperty.
						rt_property.
						icon.
						icon0);
			if(prop_list->UnihanProperty.rt_property.icon.icon1)
				new_property->UnihanProperty.rt_property.icon.icon1
						= strdup(prop_list->UnihanProperty.
						rt_property.
						icon.
						icon1);

		} else if(prop_list->type == UNIHAN_RPOPERTY_USER) {
			new_property->type = prop_list->type;
			new_property->UnihanProperty.u_property.size
					= prop_list->UnihanProperty.u_property.size;
			new_property->UnihanProperty.u_property.priv_data
					= (void*)malloc(prop_list->UnihanProperty.u_property.size+1);
			memcpy(new_property->UnihanProperty.u_property.priv_data,
					prop_list->UnihanProperty.u_property.priv_data,
					prop_list->UnihanProperty.u_property.size);
		} else {
			prop_list = prop_list->next;
			continue;
		}

		new_property->next = newlist;
		newlist = new_property;
		prop_list = prop_list->next;
	}
	return newlist;
}

void
toggle_width(iml_session_t *s)
{
	UnihanPropertyArgList prop;

	prop = im_get_runtime_property(s, UNIHAN_RUNTIME_PROPERTY_FULL_HALF, 0);

	if(prop->type) {
		if(prop->UnihanProperty.rt_property.value) {
			prop->UnihanProperty.rt_property.value = 0;
			im_set_width(s ,0);
			status_update_width(s, 0);
		} else {
			prop->UnihanProperty.rt_property.value = 1;
			im_set_width(s ,1);
			status_update_width(s, 1);
		}
	}
}

void
toggle_puncce(iml_session_t *s)
{
	UnihanPropertyArgList prop;

	prop = im_get_runtime_property(s, UNIHAN_RUNTIME_PROPERTY_PUNC_EN_CH, 0);

	if(prop->type) {
		if(prop->UnihanProperty.rt_property.value) {
			prop->UnihanProperty.rt_property.value = 0;
			im_set_puncce(s ,0);
			status_update_puncce(s, 0);
		} else {
			prop->UnihanProperty.rt_property.value = 1;
			im_set_puncce(s ,1);
			status_update_puncce(s, 1);
		}
	}
}
#endif

/****************************************************************************/
/**
 * @brief Propagates lookup page turn event to IM
 * @ingroup IM
 *
 * @param s        current IMLSession
 * @param direction  1 = next page, -1 = previous page
 *
 * @returns %True if handled by IM, %False otherwise.
 */
int
im_propagate_lookup_page_turn_event(iml_session_t *s, int direction)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im = (IMList *)scdata->currIM;

	// ensure IM is init'd for desktop and session
	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
        init_uim(&uim, im, s);

		IMLookupPageTurnEvent imev;
		imev.type = IM_LOOKUP_PAGE_TURN;
		imev.direction = direction;

		return im->m->handle_event(&uim, (IMEvent *)&imev);
	}
	return False;
}


/****************************************************************************/
/**
 * @brief Propagates lookup selection event to IM
 * @ingroup IM
 *
 * @param s     current IMLSession
 * @param num   lookup choice number selected
 *
 * @returns %True if handled by IM, %False otherwise.
 */
int
im_propagate_lookup_select_event(iml_session_t *s, int num)
{
	DataPerSession *scdata = (DataPerSession *)s->specific_data;
	DataPerDesktop *dtdata = (DataPerDesktop *)s->desktop->specific_data;
	IMManagerData *d = (IMManagerData *)dtdata->im_manager_data;
	IMList *im = (IMList *)scdata->currIM;

	// ensure IM is init'd for desktop and session
	if (prepare_im(im, s, d, scdata)) {
		UnihanIM uim;
        init_uim(&uim, im, s);

		IMLookupSelectEvent imev;
		imev.type = IM_LOOKUP_SELECT;
		imev.num = num;

		return im->m->handle_event(&uim, (IMEvent *)&imev);
	}
	return False;
}


#if TIMEBOMB
/****************************************************************************/
int
check_expired(iml_desktop_t *d)
{
        DataPerDesktop *dd = (DataPerDesktop *)d->specific_data;

        if ((dd->expired & 0x01) == 0x01)
                return 1;

        time_t now = time(NULL);

        if (now >= EXPIREDATE) {
                dd->expired |= 1;
        } else if (now < 1073779200) {  // Sun Jan 11 00:00:00 UTC 2004
                dd->expired |= 1;
        }

        return ((dd->expired & 0x01) == 0x01);
}

/****************************************************************************/
void
notify_timebomb(iml_session_t *s)
{
        DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;
        int intval[3];
        intval[0] = UNIHAN_ENGINE_DEMO_EXPIRED;
        intval[1] = dd->expired;
        intval[2] = EXPIREDATE;
        aux_draw(s, AUX_STATUS_CLASS_NAME, 3, intval, 0, NULL);
}

/****************************************************************************/
void
notify_expired(iml_session_t *s)
{
        DataPerDesktop *dd = (DataPerDesktop *)s->desktop->specific_data;

        if ((dd->expired & 0x02) == 0x02)
                return;
        notify_timebomb(s);
        dd->expired |= 0x02;
}
#endif

// vi:ts=4:sw=4:nowrap:cin:expandtab
